package com.my.admin.aspect;

import cn.hutool.core.util.StrUtil;
import com.my.admin.annotation.CleanCache;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;

@Component
@Aspect
@Slf4j
public class ClearCacheAspect {

    @Resource
    RedisTemplate<String, Object> redisTemplate;

    @Pointcut(value = "(execution(* *.*(..)) && @annotation(com.my.admin.annotation.CleanCache))")
    private void pointcut() {}

    @AfterReturning(value = "pointcut()")
    private void process(JoinPoint joinPoint) {
        Object target = joinPoint.getTarget();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        CleanCache cleanCache = method.getAnnotation(CleanCache.class);
        if(cleanCache != null) {
            if(cleanCache.classCache()) {
                cleanRedisCache("*" + target.getClass().getName() + "*");
            }
            String value = cleanCache.value();
            if(StrUtil.isNotBlank(value)) {
                cleanRedisCache("*" + value + "*");
            }
            String[] keys = cleanCache.key();
            redisTemplate.delete(Arrays.asList(keys));
        }
    }

    private void cleanRedisCache(String key) {
        if(key != null) {
            Set<String> set = keys(key);
            if(set != null) {
                redisTemplate.delete(set);
                log.info("清理" + key + "缓存");
            }
        }
    }

    public Set<String> keys(String pattern) {
        Set<String> set = new HashSet<>();
        this.scan(pattern, item -> set.add(new String(item, StandardCharsets.UTF_8)));
        return set;
    }

    public void scan(String pattern, Consumer<byte[]> consumer) {
        redisTemplate.execute((RedisConnection connection) -> {
            try (Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().count(1000).match(pattern).build())){
                cursor.forEachRemaining(consumer);
            } catch (IOException e) {
                e.printStackTrace();
                log.error("匹配redis Key 发生错误！", e);
            }
            return null;
        });
    }

}
